package cn.cosmosx.base.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import cn.cosmosx.base.constant.ResultCode;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Optional;

@Slf4j
public final class ResponseUtils {
    private static final String RESPONSE_CONTENT_TYPE = "application/json;charset=UTF-8";
    private static final Charset CHARSET = StandardCharsets.UTF_8;

    private static void preHandle(HttpServletResponse response) {
        response.setContentType(RESPONSE_CONTENT_TYPE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    }

    public static void wrapResponse(HttpServletResponse response, ResultCode resultCode) throws IOException {
        wrapResponse(response, resultCode.name(), resultCode.getMessage());
    }

    public static void wrapResponse(HttpServletResponse response, ResultCode resultCode, String message) throws IOException {
        wrapResponse(response, resultCode.name(), message);
    }

    /**
     * 王客户端写数据
     * <p>
     * 建议从 HttpServletResponse调用 response.getOutputStream() 来写数据而不是调用 response.getWriter()
     *
     * @param response
     * @param code
     * @param message
     * @throws IOException
     */
    public static void wrapResponse(HttpServletResponse response, String code, String message) throws IOException {
        preHandle(response);
        response.setStatus(HttpStatus.OK.value());
        try (final ServletOutputStream out = response.getOutputStream()) {
            out.write(SecurityResponseBuilder
                    .of(code, message, null).build()
                    .getBytes(CHARSET)
            );
            out.flush();
        }
    }

    /**
     * 无权限访问
     *
     * @param response
     * @throws IOException
     */
    public static void unauthorizedResponse(HttpServletResponse response) throws IOException {
        wrapResponse(response, ResultCode.U00004);
    }

    /**
     * 访问成功
     *
     * @param response
     * @throws IOException
     */
    public static void successfulResponse(HttpServletResponse response) throws IOException {
        // 登录失败HTTP状态码返回200，由前端根据resultCode判断
        wrapResponse(response, ResultCode.S, "登录成功");
    }

    /**
     * 设置文件二进制流响应头信息
     *
     * @param response - 响应
     * @param fileName - 文件名称（包含后缀）
     */
    public static void setBlobHeader(HttpServletResponse response, String fileName) throws Exception {
        // 设置响应头参数
        response.setCharacterEncoding(CHARSET.name());
        response.setContentType("application/octet-stream;charset=" + CHARSET.name());
        // 这里URLEncoder.encode可以防止中文乱码
        String urlFileName = URLEncoder.encode(fileName, CHARSET.name()).replaceAll("\\+", "%20");
        response.setHeader("content-disposition", "attachment;filename=" + urlFileName);
        //控制浏览器不要缓存
        response.setHeader("Expires", "-1");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
    }

    private ResponseUtils() {
    }

    final static class SecurityResponseBuilder {
        private static final Object EMPTY_DATA = new Object();
        public String code;
        public String message;
        public Object data;

        private SecurityResponseBuilder(String code, String message, Object data) {
            this.code = Optional.ofNullable(code).orElse(ResultCode.S.name());
            this.message = Optional.ofNullable(message).orElse(ResultCode.S.getMessage());
            this.data = Optional.ofNullable(data).orElse(EMPTY_DATA);
        }

        static SecurityResponseBuilder of(String code, String message, Object data) {
            return new SecurityResponseBuilder(code, message, data);
        }

        String build() {
            JSONObject jsonObject = new JSONObject(new LinkedHashMap<>(3));
            jsonObject.put("code", this.code);
            jsonObject.put("message", this.message);
            jsonObject.put("data", JSONObject.toJSONString(this.data));
            return jsonObject.toString(SerializerFeature.WriteNullStringAsEmpty);
        }
    }
}

